﻿using System;
using System.Drawing;
 
// Source:
//  http://www.vbaccelerator.com/home/NET/Code/Libraries/Graphics/HLS_to_RGB/Hlsrgb_zip_HLSRGB_HLSRGBVB_HLSRGB_vb.asp
 
public class ColorHelper
{
 
    private byte m_red = 0;
    private byte m_green = 0;
    private byte m_blue = 0;
 
    private double m_hue = 0;
    private double m_luminance = 0;
    private double m_saturation = 0;
 
    public ColorHelper(byte red, byte green, byte blue)
    {
        m_red = red;
        m_green = green;
        m_blue = blue;
    }
 
    public ColorHelper(ColorHelper hlsrgb)
    {
        m_red = hlsrgb.Red;
        m_blue = hlsrgb.Blue;
        m_green = hlsrgb.Green;
        m_luminance = hlsrgb.Luminance;
        m_hue = hlsrgb.Hue;
        m_saturation = hlsrgb.Saturation;
    }
 
    public ColorHelper(Color c)
    {
        m_red = c.R;
        m_green = c.G;
        m_blue = c.B;
        ToHLS();
    }
 
    public ColorHelper(string htmlColor) : this(ColorTranslator.FromHtml(htmlColor))
    {
    }
 
    private ColorHelper()
    {
    }
 
    public byte Red
    {
        get { return m_red; }
        set { m_red = value; ToHLS(); }
    }
 
    public byte Green
    {
        get { return m_green; }
        set { m_green = value; ToHLS(); }
    }
 
    public byte Blue
    {
        get { return m_blue; }
        set { m_blue = value; ToHLS(); }
    }
 
    public double Luminance
    {
        get { return m_luminance; }
        set
        {
            if (((value < 0f) | (value > 1f)))
            {
                throw new ArgumentOutOfRangeException("Luminance", "Luminance must be between 0.0 and 1.0");
            }
            m_luminance = value;
            ToRGB();
        }
    }
 
    public double Hue
    {
        get { return m_hue; }
        set
        {
            if (((value < 0f) | (value > 360f)))
            {
                throw new ArgumentOutOfRangeException("Hue", "Hue must be between 0.0 and 360.0");
            }
            m_hue = value;
            ToRGB();
        }
    }
 
    public double Saturation
    {
        get { return m_saturation; }
        set
        {
            if (((value < 0f) | (value > 1f)))
            {
                throw new ArgumentOutOfRangeException("Saturation", "Saturation must be between 0.0 and 1.0");
            }
            m_saturation = value;
            ToRGB();
        }
    }
 
    public Color Color
    {
        get { return Color.FromArgb(m_red, m_green, m_blue); }
        set
        {
            m_red = value.R;
            m_green = value.G;
            m_blue = value.B;
            ToHLS();
        }
    }
 
    public void LightenColor(double lightenBy)
    {
        m_luminance *= (1f + lightenBy);
        if ((m_luminance > 1f))
        {
            m_luminance = 1f;
        }
        ToRGB();
    }
 
    public void DarkenColor(double darkenBy)
    {
        m_luminance *= darkenBy;
        ToRGB();
    }
 
    public ColorHelper(double hue, double luminance, double saturation)
    {
        if (((saturation < 0f) | (saturation > 1f)))
        {
            throw new ArgumentOutOfRangeException("Saturation", "Saturation must be between 0.0 and 1.0");
        }
        if (((hue < 0f) | (hue > 360f)))
        {
            throw new ArgumentOutOfRangeException("Hue", "Hue must be between 0.0 and 360.0");
        }
        if (((luminance < 0f) | (luminance > 1f)))
        {
            throw new ArgumentOutOfRangeException("Luminance", "Luminance must be between 0.0 and 1.0");
        }
        m_hue = hue;
        m_luminance = luminance;
        m_saturation = saturation;
        ToRGB();
    }
 
    private void ToHLS()
    {
        byte minval = Math.Min(m_red, Math.Min(m_green, m_blue));
        byte maxval = Math.Max(m_red, Math.Max(m_green, m_blue));
        double mdiff = (maxval * 1.0 - minval * 1.0);
        double msum = (maxval * 1.0 + minval * 1.0);
        m_luminance = msum / 510f;
        if ((maxval == minval))
        {
            m_saturation = 0f;
            m_hue = 0f;
        }
        else
        {
            double rnorm = (maxval - m_red) / mdiff;
            double gnorm = (maxval - m_green) / mdiff;
            double bnorm = (maxval - m_blue) / mdiff;
            if ((m_luminance <= 0.5f))
            {
                m_saturation = (mdiff / msum);
            }
            else
            {
                m_saturation = (mdiff / (510f - msum));
            }
            if ((m_red == maxval))
            {
                m_hue = 60f * (6f + bnorm - gnorm);
            }
            if ((m_green == maxval))
            {
                m_hue = 60f * (2f + rnorm - bnorm);
            }
            if ((m_blue == maxval))
            {
                m_hue = 60f * (4f + gnorm - rnorm);
            }
            if ((m_hue > 360f))
            {
                m_hue = m_hue - 360f;
            }
        }
    }
 
    private void ToRGB()
    {
        if ((m_saturation == 0.0))
        {
            m_red = Convert.ToByte(m_luminance * 255f);
            m_green = m_red;
            m_blue = m_red;
        }
        else
        {
            double rm1;
            double rm2;
            if ((m_luminance <= 0.5f))
            {
                rm2 = m_luminance + m_luminance * m_saturation;
            }
            else
            {
                rm2 = m_luminance + m_saturation - m_luminance * m_saturation;
            }
            rm1 = 2f * m_luminance - rm2;
            m_red = ToRGB1(rm1, rm2, m_hue + 120f);
            m_green = ToRGB1(rm1, rm2, m_hue);
            m_blue = ToRGB1(rm1, rm2, m_hue - 120f);
        }
    }
 
    private byte ToRGB1(double rm1, double rm2, double rh)
    {
        if ((rh > 360f))
        {
            rh -= 360f;
        }
        else if ((rh < 0f))
        {
            rh += 360f;
        }
        if ((rh < 60f))
        {
            rm1 = rm1 + (rm2 - rm1) * rh / 60f;
        }
        else if ((rh < 180f))
        {
            rm1 = rm2;
        }
        else if ((rh < 240f))
        {
            rm1 = rm1 + (rm2 - rm1) * (240f - rh) / 60f;
        }
        return Convert.ToByte(rm1 * 255);
    }
}
